package com.example.readerapp.myandserver.util;

import java.io.BufferedReader;
import java.io.FileReader;
import java.text.NumberFormat;
import java.util.*;

public class FindWordFromDir
{
    //使用何种字符串匹配算法
    public static NaiveStringMatching naiveStringMatching=new NaiveStringMatching();
    public static Levenshtein levenshtein=new Levenshtein();
    public static 新牛津英汉双解词典 新牛津英汉双解词典=new 新牛津英汉双解词典();
    public static class FindWordResult{
        public ArrayList<String> directContain;
        public ArrayList<String> indirectContain;
        FindWordResult()
        {
            this.indirectContain=new ArrayList<>();
            this.directContain=new ArrayList<>();
        }
    }
    /**
     * 在词典中查词
     *  FindWordFromDir.FindWordResult result=ImitateFindWordFromFile(FindWordFromDir.新牛津英汉双解词典,"C:\\Users\\ASUS\\Desktop\\其他\\英语学习资料\\新牛津英汉双解词典.txt", "ball",FindWordFromDir.levenshtein);
     *         if(result.directContain.size()>0)
     *         {
     *             System.out.println("直接包含：\n"+result.directContain);
     *         }
     *         else if(result.indirectContain.size()>0)
     *         {
     *             System.out.println("间接包含：\n"+result.indirectContain);
     *         }
     *         else
     *             System.err.println("没找到！");
     * @param getWordAndTranslateFromDir 在那个单词书使用何种算法读取此单词书
     * @param wordBookPath 读取单词书的位置
     * @param word 待查找单词
     * @param stringMatchingAlgorithm 在从单词书查找单词时使用何种字符串匹配算法
     * */
    public static FindWordResult ImitateFindWordFromFile(GetWordAndTranslateFromDir getWordAndTranslateFromDir,String wordBookPath,String word,StringMatchingAlgorithm stringMatchingAlgorithm) throws Exception
    {
        FindWordResult findWordResult=new FindWordResult();
        word=word.toLowerCase();
        //System.err.println(new SimpleDateFormat("yyyy-MM-dd:hh:mm:ss").format(new Date())+" 开始单词查询");
        //读取单词书的每个单词及其释义
        Map<String,String> Dir= getWordAndTranslateFromDir.Algorithm(wordBookPath);
        Set<Map.Entry<String,String>> entrySet= Dir.entrySet();

        /*key:查找到的释义，value:相似度*/
        Map<String,Double> resultMap=new LinkedHashMap<>();
        //将含有待查找单词的释义存储，以delta标记释义和待查找单词的相似度
        for(Map.Entry entry:entrySet)
        {
            String sentence= (String) entry.getKey();//获取词典中的单词
            //其实直接定义一个只有Integer和Boolean的类就行了，但是为了加深对这个类的理解，就用它了
            Double delta=null;
            //比较词典单词与待查找单词的相似度
            delta= stringMatchingAlgorithm.Algorithm(word.toLowerCase(),sentence);
            if(delta!=null)
            {
                //进一步进行排序比较，放入直接包含
                resultMap.put((String) entry.getValue(), delta);
            }
            //如果词典单词中含有待查单词放入间接包含
            if(sentence.contains(word.toLowerCase()))
            {
                findWordResult.indirectContain.add((String) entry.getValue());
            }
        }
        //委婉的实现按值排序，按照相似度由高到地排序
        List<Map.Entry<String,Double>> list=new ArrayList<>(resultMap.entrySet());//就是把键和值以字段的方式包装成一个Map.Entry类
        list.sort(new Comparator<Map.Entry<String, Double>>()
        {
            @Override
            public int compare(Map.Entry<String,Double> o1, Map.Entry<String,Double> o2)
            {
                return o2.getValue().compareTo(o1.getValue());
            }
        });//从小到大

        //直接包含最多保存前15个,因为那个高级字符串匹配算法效率太高了,要是用那个朴素字符串匹配算法，就最好把所有结果都保存一下...
        for(int i=0;i< list.size()&&i<10;i++)
        {
            findWordResult.directContain.add(list.get(i).getKey());
        }
       // System.err.println(new SimpleDateFormat("yyyy-MM-dd:hh:mm:ss").format(new Date())+" 结束单词查询");
        return findWordResult;
    }

    /**读取单词书中的单词*/
    interface GetWordAndTranslateFromDir
    {
        /**
         * key:单词
         * value:释义
         * */
        Map<String,String> Algorithm(String filePath) throws Exception;
    }
    private static Map<String,String> xingNiuJin=null;
    static class 新牛津英汉双解词典 implements GetWordAndTranslateFromDir{
        /*将单词书的每一个单词的释义单独保存*/
        @Override
        public Map<String,String> Algorithm(String filePath) throws Exception
        {
            if(xingNiuJin==null)
            {
                Map<String, String> result = new HashMap<>();
                //91296个单词
                BufferedReader reader = new BufferedReader(new FileReader(filePath));
                String s;
                //  ArrayList<String> book=new ArrayList<String>();
                String imm = "";
                while ((s = reader.readLine()) != null)
                {
                    if (!s.equals("\uFEFF————————————") && !s.equals("————————————"))
                    {
                        imm = imm + s + "\n";
                    }
                    else
                    {
                        result.put(imm.split("\n")[0].replaceAll("☆", "").replaceAll("★", ""), imm);
                        // book.add(imm);
                        imm = "";
                    }
                }
                reader.close();
                xingNiuJin=result;
                return result;
            }else
            {
                return xingNiuJin;
            }
        }
    }
    /**使用何种字符串匹配算法*/
    interface StringMatchingAlgorithm
    {
        /**返回值是两个文本的相似度,规定如果返回null说明相似度太低*/
        Double Algorithm(String str1,String str2);
    }
    //Levenshtein Distance莱文斯坦距离
    static class  Levenshtein implements StringMatchingAlgorithm{
        /*很高级的字符串相似度匹配算法,若相似度大于0.3则返回相似度*/
        @Override
        public Double Algorithm(String str1, String str2) {
            //计算两个字符串的长度。
            int len1 = str1.length();
            int len2 = str2.length();
            //建立上面说的数组，比字符长度大一个空间
            int[][] dif = new int[len1 + 1][len2 + 1];
            //赋初值，步骤B。
            for (int a = 0; a <= len1; a++) {
                dif[a][0] = a;
            }
            for (int a = 0; a <= len2; a++) {
                dif[0][a] = a;
            }
            //计算两个字符是否一样，计算左上的值
            int temp;
            for (int i = 1; i <= len1; i++) {
                for (int j = 1; j <= len2; j++) {
                    if (str1.charAt(i - 1) == str2.charAt(j - 1)) {
                        temp = 0;
                    } else {
                        temp = 1;
                    }
                    //取三个值中最小的
                    dif[i][j] = min(dif[i - 1][j - 1] + temp, dif[i][j - 1] + 1,
                                    dif[i - 1][j] + 1);
                }
            }
            // System.out.println("字符串\""+str1+"\"与\""+str2+"\"的比较");
            //取数组右下角的值，同样不同位置代表不同字符串的比较
            //System.out.println("差异步骤："+dif[len1][len2]);
            //计算相似度
            Double similarity =1 - (double) dif[len1][len2] / Math.max(str1.length(), str2.length());
            //System.out.println("相似度："+similarity);
            if(similarity<0.3)
                return null;
            else
            {
                //    System.out.println("相似度："+getPercentValue(similarity));
                return similarity;
            }

        }
        //得到最小值
        private int min(int... is) {
            int min = Integer.MAX_VALUE;
            for (int i : is) {
                if (min > i) {
                    min = i;
                }
            }
            return min;
        }
        private String getPercentValue( float similarity){
            NumberFormat fmt = NumberFormat.getPercentInstance();
            fmt.setMaximumFractionDigits(2);//最多两位百分小数，如25.23%
            return fmt.format(similarity);
        }

    }
    static class NaiveStringMatching implements StringMatchingAlgorithm
    {
        /**
         * 朴素字符串匹配算法
         * 简单的认为句子与单词之间字符数目越接近，他们就越相似，函数返回二者字符数目的差值
         *
         * @return*/
        @Override
        public Double Algorithm(String word, String sentence)
        {
            char[] w=word.toCharArray();
            char[] c=sentence.toCharArray();
            for(int i=0;i<c.length;i++)
            {
                if(c[i]==w[0])
                {
                    //单词的前一个字符
                    char before = 0;
                    //默认不存在前一个字符
                    boolean beforeL=true;
                    //单词的后一个字符
                    char end=0;
                    //默认不存在后一个字符
                    boolean endL=true;
                    //存在前一个字符
                    if(i-1>=0)
                    {
                        before=c[i-1];
                        beforeL=false;
                    }
                    //默认子串与单词匹配
                    boolean patternS=true;
                    int j=1;
                    //由于条件(i+j)<c.length,试想句子：sda 单词：arf,当i==2时，i+1=3,直接不进入循环，此时patternS仍为true,所以需要确保匹配的字符数目长度为w.length
                    for(;j<w.length&&(i+j)<c.length;j++)
                    {
                        if(c[i+j]!=w[j])
                        {
                            patternS=false;
                        }
                    }
                    //存在后一个字符
                    if(i+j<c.length)
                    {
                        end=c[i+j];
                        endL=false;
                    }
                    if(patternS&&j==w.length)
                    {
                        if(((end+"").matches("[^a-zA-Z]")||endL)&&((before+"").matches("[^a-zA-Z]")||beforeL))
                        {
                       /* System.out.print("匹配:");
                        if (!beforeL)
                        {
                            System.out.print(before);
                        }
                        System.out.print(word);
                        if (!endL)
                        {
                            System.out.print(end);
                        }
                        System.out.println();
                        */
                            return  (double)(c.length-w.length);
                        }

                    }
                }
            }
            return null;

        }

    }
}


